package com.xdoc.client;

import com.xdoc.client.util.IOUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import java.io.*;
import java.util.HashMap;
import java.util.Map;

/**
 * XDoc客户端
 * @author coffish
 */
public class XDocClient {
	private final static Log logger = LogFactory.getLog(XDocClient.class);

	/**
	 * 默认服务器地址
	 */
	public static String DEFAULT_URL = "https://xdoc.xdocin.com";
	/**
	 * XDOC服务地址
	 */
	private String url;
	/**
	 * 服务地址
	 * @return
	 */
	public String getUrl() {
		return url;
	}
	/**
	 * 服务地址
	 * @param url
	 */
	public void setUrl(String url) {
		this.url = url;
	}
	/**
	 * 注册邮箱地址
	 */
	private String mail;
	/**
	 * 注册邮箱地址
	 * @return
	 */
	public String getMail() {
		return mail;
	}
	/**
	 * 注册邮箱地址
	 * @param mail
	 */
	public void setMail(String mail) {
		this.mail = mail;
	}
	/**
	 * 密码
	 */
	private String key;
	/**
	 * 密码
	 * @return
	 */
	public String getKey() {
		return key;
	}
	/**
	 * 密码
	 * @param key
	 */
	public void setKey(String key) {
		this.key = key;
	}
	/**
	 * token有效期（分钟）
	 */
	private int tokenExpire = 8 * 60; //8小时

	/**
	 * token有效期（分钟）
	 * @return
	 */
	public int getTokenExpire() {
		return tokenExpire;
	}

	/**
	 * token有效期（分钟）
	 * @param tokenExpire
	 */
	public void setTokenExpire(int tokenExpire) {
		this.tokenExpire = tokenExpire;
	}
	/**
	 * 构造器
	 */
	public XDocClient() {
		this(DEFAULT_URL); 
	}
	/**
	 * 构造器
	 * @param url 服务地址
	 */
	public XDocClient(String url) {
		this.url = url;
		this.mail = "";
		this.key = "";
	}
	/**
	 * 运行XDOC
	 * @param xdoc XDOC文件
	 * @param data 数据
	 * @param targetFile 目标文件
	 * @throws IOException
	 */
	public void run(String xdoc, Object data, File targetFile) throws IOException {
		this.run(xdoc, data, new FileOutputStream(targetFile), IOUtils.getFileFormat(targetFile.getName()));
	}
	/**
	 * 运行XDOC
	 * @param xdoc XDOC文件
	 * @param data 数据
	 * @param out 输出目标
	 * @param format 目标格式
	 * @throws IOException
	 */
	public void run(String xdoc, Object data, OutputStream out, String format) throws IOException {
		if (!xdoc.endsWith(".xdoc")) {
			throw new IOException("无效XDOC文件：" + xdoc);
		}
		try {
			Map<String, Object> param = new HashMap<String, Object>();
			param.put("func", "run");
			param.put("xdoc", xdoc);
			param.put("data", data);
			param.put("format", format);
			param.put("subformat", "json");
			invoke(param, out);
		} finally {
			out.close();
		}
	}
	/**
	 * 获取模板元数据
	 * @param xdoc XDOC文件
	 * @throws IOException
	 */
	public Map<String, String> meta(String xdoc) throws IOException {
		Map<String, Object> param = new HashMap<String, Object>();
		param.put("func", "meta");
		param.put("xdoc", xdoc);
		param.put("format", "json");
		return (Map<String, String>) invoke(param);
	}
	/**
	 * 获取令牌
	 * @param type
	 * @param idName
	 * @param idVal
	 * @return
	 * @throws IOException
	 */
	private String getToken(String type, String idName, String idVal) throws IOException {
		Map<String, Object> param = new HashMap<String, Object>();
		param.put("func", "token");
		param.put("mail", this.mail);
		param.put("key", this.key);
		param.put("type", type);
		if (idName != null && idName.length() > 0) {
			param.put(idName, idVal);
		}
		param.put("expire", this.tokenExpire);
		param.put("format", "json");
		return (String) invoke(param);
	}
	/**
	 * 获取编辑令牌
	 * @param xdoc
	 * @return
	 * @throws IOException
	 */
	public String getEditToken(String xdoc) throws IOException {
		return this.getToken("edit", "xdoc", xdoc);
	}
	/**
	 * 获取编辑URL
	 * @param xdoc
	 * @return
	 * @throws IOException
	 */
	public String getEditUrl(String xdoc) throws IOException {
		StringBuilder sb = new StringBuilder(this.getXUrl());
		sb.append("?func=edit&token=");
		sb.append(this.getEditToken(xdoc));
		return sb.toString();
	}
	/**
	 * 获取ID数据令牌
	 * @param id
	 * @return
	 * @throws IOException
	 */
	public String getIdDataToken(String id) throws IOException {
		return this.getToken("data", "id", id);
	}
	/**
	 * 获取URL数据令牌
	 * @param url
	 * @return
	 * @throws IOException
	 */
	public String getUrlDataToken(String url) throws IOException {
		return this.getToken("data", "url", url);
	}
	/**
	 * 获取数据
	 * @param dataToken 数据令牌
	 * @return
	 * @throws IOException
	 */
	public String getData(String dataToken) throws IOException {
		Map<String, Object> param = new HashMap<String, Object>();
		param.put("func", "getdata");
		param.put("token", dataToken);
		param.put("format", "json");
		return (String) invoke(param);
	}
	/**
	 * 提交数据
	 * @param dataToken 数据令牌
	 * @param data 数据
	 * @return
	 * @throws IOException
	 */
	public void postData(String dataToken, Object data) throws IOException {
		Map<String, Object> param = new HashMap<String, Object>();
		param.put("func", "postdata");
		param.put("token", dataToken);
		param.put("data", data);
		param.put("format", "json");
		invoke(param);
	}
	/**
	 * 获取表单填报URL
	 * @param xdoc
	 * @param dataToken 数据令牌
	 * @return
	 * @throws IOException
	 */
	public String getFormUrl(String xdoc, String dataToken) throws IOException {
		StringBuilder sb = new StringBuilder(this.getXUrl());
		sb.append("?func=form&xdoc=").append(IOUtils.urlEncode(xdoc));
		sb.append("&dtoken=").append(dataToken);
		return sb.toString();
	}
	/**
	 * 上传模板文件
	 * @param file
	 * @throws IOException
	 */
	public void up(File file) throws IOException {
		this.up(file, true);
	}
	/**
	 * 上传模板文件
	 * @param file
	 * @throws IOException
	 */
	private void up(File file, boolean check) throws IOException {
		if (file.isFile()) {
			if (file.getName().endsWith(".xdoc")) {
				this.up(file.getName(), new FileInputStream(file));
			} else if (check) {
				throw new IOException(file.getName() + "不是有效的模板文件");
			}
		} else if (file.isDirectory()) {
			File[] list = file.listFiles();
			for (File file2 : list) {
				this.up(file2, false);
			}
		}
	}
	/**
	 * 上传模板文件
	 * @param xdoc
	 * @param in
	 * @throws IOException
	 */
	public void up(String xdoc, InputStream in) throws IOException {
		logger.info("上传模板文件：" + xdoc);
		String token = this.getEditToken(xdoc);
		Map<String, Object> param = new HashMap<String, Object>();
		param.put("func", "up");
		param.put("data", IOUtils.toDataURI(in, "xdoc"));
		param.put("token", token);
		param.put("format", "json");
		this.invoke(param);
	}
	/**
	 * 上传模板文件
	 * @param xdoc
	 * @throws IOException
	 */
	public void remove(String xdoc) throws IOException {
		logger.info("删除模板文件：" + xdoc);
		String token = this.getEditToken(xdoc);
		Map<String, Object> param = new HashMap<String, Object>();
		param.put("func", "remove");
		param.put("token", token);
		param.put("format", "json");
		this.invoke(param);
	}
	/**
	 * 下载模板文件
	 * @param xdoc
	 * @param out
	 * @throws IOException
	 */
	public void down(String xdoc, OutputStream out) throws IOException {
		logger.info("下载模板文件：" + xdoc);
		Map<String, Object> param = new HashMap<String, Object>();
		param.put("func", "down");
		param.put("mail", this.mail);
		param.put("xdoc", xdoc);
		param.put("format", "json");
		this.invoke(param, out);
	}
	/**
	 * 模板文件是否存在
	 * @param xdoc
	 * @throws IOException
	 */
	public boolean exists(String xdoc) throws IOException {
		Map<String, Object> param = new HashMap<String, Object>();
		param.put("func", "exists");
		param.put("mail", this.mail);
		param.put("xdoc", xdoc);
		param.put("format", "json");
		return "true".equals(this.invoke(param));
	}

	/**
	 * 格式转换
	 * @param xdocFile xdoc文件
	 * @param tarFile 目标文件，格式支持：pdf/ofd/png/jpg
	 * @throws IOException
	 */
	public void to(File xdocFile, File tarFile) throws IOException {
		if (xdocFile.getName().endsWith(".xdoc")) {
			this.to(IOUtils.toDataURI(new FileInputStream(xdocFile)), tarFile);
		} else {
			throw new IOException("无效XDOC文件：" + xdocFile.getName());
		}
	}

	/**
	 * 格式转换
	 * @param xdoc xdoc文件，支持服务器端xdoc地址/http/datauri
	 * @param tarFile 目标文件，格式支持：pdf/ofd/png/jpg
	 * @throws IOException
	 */
	public void to(String xdoc, File tarFile) throws IOException {
		this.to(xdoc, new FileOutputStream(tarFile), IOUtils.getFileFormat(tarFile.getName()));
	}

	/**
	 * 格式转换
	 * @param xdoc xdoc文件，支持服务器端xdoc地址/http/datauri
	 * @param out 输出流
	 * @param format 目标格式，支持：pdf/ofd/png/jpg
	 * @throws IOException
	 */
	public void to(String xdoc, OutputStream out, String format) throws IOException {
		if (xdoc.endsWith(".xdoc") || xdoc.startsWith("data:") || xdoc.startsWith("http:") || xdoc.startsWith("https:")) {
			try {
				Map<String, Object> param = new HashMap<String, Object>();
				param.put("func", "to");
				param.put("xdoc", xdoc);
				param.put("token", this.getToken("to", null, null));
				param.put("format", format);
				param.put("subformat", "json");
				invoke(param, out);
			} finally {
				out.close();
			}
		} else {
			throw new IOException("无效XDOC文档地址：" + xdoc);
		}
	}
	private String getXUrl() {
		return this.url + (this.url.endsWith("/") ? "xdoc" : "/xdoc");
	}
	private void invoke(Map<String, Object> param, OutputStream out) throws IOException {
		ApiInvoker.invoke(this.getXUrl(), param, out);
	}
	private Object invoke(Map<String, Object> param) throws IOException {
		return ApiInvoker.invoke(this.getXUrl(), param);
	}
}
